﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Messaging;
using ShellCommandService.Contracts.DataContracts;

namespace ShellCommandService.MSMQ
{
    public class ShellCommandQueueRepository : IShellCommandQueueRepository
    {
        private MessageQueue messageQueue = null;
        private readonly string MessageQueueName;

        /// <summary>
        /// The variable is declared to be volatile to ensure that assignment to the 
        /// LoggerMSMQ variable completes before the instance variable can be accessed.
        /// </summary>
        private static volatile ShellCommandQueueRepository shellCommandQueueRepository;

        /// <summary>
        /// Synchronization object.
        /// </summary>
        private static object syncObject = new object();

        /// <summary>
        /// Initializes a new instance of the <see cref="MSMQLogger"/> class.
        /// </summary>
        private ShellCommandQueueRepository()
        {
            MessageQueueName = ConfigurationManager.AppSettings["ShellCommandQueueName"];
            CreateMessageQueue();
        }

        /// <summary>
        /// Gets the instance is private static property to return the same Instance of the class every time.
        /// Note: Double - checked serialized initialization of class pattern is used.
        /// </summary>
        /// <value>LoggerMSMQ instance.</value>
        public static ShellCommandQueueRepository Instance
        {
            get
            {
                // Check for null before acquiring the lock.
                if (shellCommandQueueRepository == null)
                {
                    // Use a syncObject to lock on, to avoid deadlocks among multiple threads.
                    lock (syncObject)
                    {
                        // Again check if log4NetInstance has been initialized, 
                        // since some other thread may have acquired the lock first and constructed the object.
                        if (shellCommandQueueRepository == null)
                        {
                            shellCommandQueueRepository = new ShellCommandQueueRepository();
                        }
                    }
                }

                return shellCommandQueueRepository;
            }
        }

        public void CreateMessageQueue()
        {
            messageQueue = null;
            if (!MessageQueue.Exists(MessageQueueName))
            {
                messageQueue = MessageQueue.Create(MessageQueueName);
            }

            else
            {
                messageQueue = new MessageQueue(MessageQueueName);
            }
        }

        public void AddShellCommandsToQueue(ShellCommandInfo info)
        {
            try
            {
                messageQueue.Send(info);
            }
            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                messageQueue.Close();
            }

        }

        /// <summary>
        /// List of the commands to fire for a machine which came from wcf service method
        /// </summary>
        /// <param name="machineName"></param>
        /// <returns></returns>
        public List<ShellCommandInfo> RetrieveLogInformationFromQueue(string machineName)
        {
            List<ShellCommandInfo> listOfShellCommandInfo = null;
            try
            {
                messageQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(ShellCommandInfo) });
                Message[] allMessages = messageQueue.GetAllMessages();
                ShellCommandInfo commandInfo = null;
                listOfShellCommandInfo = new List<ShellCommandInfo>();
                foreach (Message currentMessage in allMessages)
                {
                    commandInfo = (ShellCommandInfo)currentMessage.Body;
                    if (commandInfo.MachineName == machineName)
                    {
                        commandInfo.MessageId = currentMessage.Id;
                        listOfShellCommandInfo.Add(commandInfo);
                    }
                }
            }

            catch (MessageQueueException ex)
            {
                // Use this for fallback option like sending email or write to text file for checking what's wrong with logging
            }

            catch (Exception ex)
            {
                // Use this for fallback option like sending email or write to text file for checking what's wrong with logging
            }
            return listOfShellCommandInfo;
        }


        public void RemoveTheMessageFromTheQueue(string messageId)
        {
            try
            {
                messageQueue.ReceiveById(messageId);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        public List<ShellCommandInfo> FetchUnexecutedCommands(string machineName)
        {
            List<ShellCommandInfo> unexecutedShellCommands = null;
            try
            {
                messageQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(ShellCommandInfo) });
                Message[] allMessages = messageQueue.GetAllMessages();
                ShellCommandInfo commandInfo = null;
                unexecutedShellCommands = new List<ShellCommandInfo>();
                foreach (Message currentMessage in allMessages)
                {
                    commandInfo = (ShellCommandInfo)currentMessage.Body;
                    if (commandInfo.MachineName == machineName)
                    {
                        commandInfo.MessageId = currentMessage.Id;
                        unexecutedShellCommands.Add(commandInfo);
                    }
                }
            }

            catch (MessageQueueException ex)
            {
                // Use this for fallback option like sending email or write to text file for checking what's wrong with logging
            }

            catch (Exception ex)
            {
                // Use this for fallback option like sending email or write to text file for checking what's wrong with logging
            }
            return unexecutedShellCommands;
        }
    }
}